package Single_threadedCPU

import (
	"container/heap"
	_ "container/heap"
	"sort"
	"sync"
	"time"
)

// Task 任务
type Task struct {
	// 任务号
	num int
	// 处理时间
	processingTime int
	// 任务执行开始时间
	enqueueTime int
}

// TaskQueue 任务队列
type TaskQueue []*Task

func (pq TaskQueue) Len() int {
	return len(pq)
}

func (pq TaskQueue) Less(i, j int) bool {
	return pq[i].processingTime < pq[j].processingTime || (pq[i].processingTime == pq[j].processingTime && pq[i].num < pq[j].num)
}

func (pq TaskQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
}

func (pq *TaskQueue) Push(task interface{}) {
	*pq = append(*pq, task.(*Task))
}

func (pq *TaskQueue) Pop() interface{} {
	item := (*pq)[(*pq).Len()-1]
	*pq = (*pq)[:(*pq).Len()-1]
	return item
}

// SingleCPU CPU
type SingleCPU struct {
	// 任务队列
	taskQueue *TaskQueue
	// cpu时钟
	clock int
	// 运行日志 记录运行的任务号
	RunLog []int
}

// Start 启动 执行一组任务
func (cpu *SingleCPU) Start(tasks []*Task) {
	cpu.taskQueue = &TaskQueue{}
	heap.Init(cpu.taskQueue)
	for i := 0; i < len(tasks); {
		if cpu.taskQueue.Len() > 0 {
			cpu.run()
		}
		if cpu.taskQueue.Len() == 0 && tasks[i].enqueueTime >= cpu.clock {
			cpu.clock = tasks[i].enqueueTime
		}
		for i < len(tasks) && cpu.clock >= tasks[i].enqueueTime {
			heap.Push(cpu.taskQueue, tasks[i])
			i++
		}
	}
}

// Close 关闭cpu，释放所有未执行的任务
func (cpu *SingleCPU) Close() {
	queueLen := cpu.taskQueue.Len()
	for i := 0; i < queueLen; i++ {
		cpu.run()
	}
}

// execute one task
func (cpu *SingleCPU) run() {
	task := heap.Pop(cpu.taskQueue).(*Task)
	cpu.clock += task.processingTime
	cpu.RunLog = append(cpu.RunLog, task.num)
}

func (cpu *SingleCPU) apply(task Task) {
	heap.Push(cpu.taskQueue, task)
}
func (cpu *SingleCPU) start() {
	defer func() {
		cpu.Close()
	}()
	var wg sync.WaitGroup
	go func() {
		wg.Add(1)
		for true {
			if time.Now().Second() >= cpu.clock {
				task := heap.Pop(cpu.taskQueue).(*Task)
				cpu.clock += task.processingTime
				cpu.RunLog = append(cpu.RunLog, task.num)
			}
		}
		wg.Done()
	}()
	wg.Wait()
}

func getOrder(tasks [][]int) []int {
	taskQueue := make([]*Task, len(tasks))
	for i, task := range tasks {
		taskQueue[i] = &Task{
			num:            i,
			processingTime: task[1],
			enqueueTime:    task[0],
		}
	}

	sort.Slice(taskQueue, func(i, j int) bool {
		return taskQueue[i].enqueueTime < taskQueue[j].enqueueTime
	})
	cpu := new(SingleCPU)
	cpu.Start(taskQueue)
	cpu.Close()
	return cpu.RunLog
}

func getOrder2(tasks [][]int) []int {
	var tasksStruct []*Task
	for i, t := range tasks {
		tasksStruct = append(tasksStruct, &Task{i, t[1], t[0]})
	}
	sort.Slice(tasksStruct, func(i, j int) bool {
		return tasksStruct[i].enqueueTime < tasksStruct[j].enqueueTime
	})
	var executeLog []int
	queue := &TaskQueue{}
	heap.Init(queue)
	for i, clock := 0, 0; i < len(tasksStruct); {
		if queue.Len() > 0 {
			task := heap.Pop(queue).(*Task)
			executeLog = append(executeLog, task.num)
			clock += task.processingTime
		}
		if queue.Len() == 0 && clock <= tasksStruct[i].enqueueTime {
			clock = tasksStruct[i].enqueueTime
		}
		for i < len(tasksStruct) && tasksStruct[i].enqueueTime <= clock {
			heap.Push(queue, tasksStruct[i])
			i++
		}
	}
	for queue.Len() > 0 {
		executeLog = append(executeLog, heap.Pop(queue).(*Task).num)
	}
	return executeLog
}
